#include "Classification.h"
#include "Team.h"
#include "ZStream.h"
#include "ServiceProvider.h"
#include "GameSettings.h"
#include "TeamManager.h"
#include "MatchData.h"

#include <cassert>

bool CompareScore(const TeamScore& t1, const TeamScore& t2)
{
	if(t1.m_score != t2.m_score)
		return t1.m_score > t2.m_score;

	const int difference1 = t1.m_outGoals - t1.m_inGoals;
	const int difference2 = t2.m_outGoals - t2.m_inGoals;
	if(difference1 != difference2)
		return difference1 > difference2;

	if(t1.m_outGoals != t2.m_outGoals)
		return t1.m_outGoals > t2.m_outGoals;

	return t1.m_teamID < t2.m_teamID;
}

Classification::Classification(ServiceProvider* i_sp) : m_sp(i_sp)
{
	assert(m_sp);
}

Classification::~Classification()
{
	Clear();
}

void Classification::Clear()
{
	m_teamScores.clear();
	Utilities::DeepClear(m_matchHistory);
}

void Classification::Initialize(const TeamVec& i_teams)
{
	TeamScore record;
	record.m_drawMathes = 0;
	record.m_inGoals = 0;
	record.m_lostMatches = 0;
	record.m_outGoals = 0;
	record.m_score = 0;
	record.m_winMatches = 0;

	Utilities::DeepClear(m_matchHistory);

	m_teamScores.clear();
	m_teamScores.reserve(i_teams.size());

	for(size_t i = 0; i < i_teams.size(); ++i)
	{
		record.m_teamID = i_teams[i]->GetID();
		m_teamScores.push_back(record);
	}
}

void Classification::Update(const MatchDataVec& i_matchScore)
{
	for(MatchDataVecCIt it = i_matchScore.begin(); it != i_matchScore.end(); ++it)
	{
		TeamScoreCollection::iterator team1Score = STL::find_if(m_teamScores.begin(), m_teamScores.end(), CompareTeamID((*it)->m_teamData[0].m_teamID));
		TeamScoreCollection::iterator team2Score = STL::find_if(m_teamScores.begin(), m_teamScores.end(), CompareTeamID((*it)->m_teamData[1].m_teamID));

		assert(team1Score != m_teamScores.end() && team2Score != m_teamScores.end());

		team1Score->m_outGoals += (*it)->m_teamData[0].m_goals;
		team2Score->m_outGoals += (*it)->m_teamData[1].m_goals;

		team1Score->m_inGoals += (*it)->m_teamData[1].m_goals;
		team2Score->m_inGoals += (*it)->m_teamData[0].m_goals;

		const bool team1win = ((*it)->m_teamData[0].m_goals > (*it)->m_teamData[1].m_goals);
		const bool team2win = ((*it)->m_teamData[0].m_goals < (*it)->m_teamData[1].m_goals);

		if(!team1win && !team2win) // check for draw
		{
			++(team1Score->m_drawMathes);
			++(team2Score->m_drawMathes);

			team1Score->m_score += 1;
			team2Score->m_score += 1;
		}
		else
		{
			TeamScoreCollection::iterator& theWinner = team1win ? team1Score : team2Score;
			TeamScoreCollection::iterator& theLoser = team1win ? team2Score : team1Score;

			++(theWinner->m_winMatches);
			++(theLoser->m_lostMatches);

			theWinner->m_score += 3;
		}

	}

	STL::sort(m_teamScores.begin(), m_teamScores.end(), CompareScore);

	Utilities::Append(m_matchHistory, i_matchScore);
}

void Classification::GetSortedTeams(TeamVec& o_teams) const
{
	o_teams.clear();
	o_teams.reserve(m_teamScores.size());

	TeamScoreCollection::const_iterator it = m_teamScores.begin();
	for(; it != m_teamScores.end(); ++it)
	{
		o_teams.push_back(m_sp->GetTeamMgr().GetTeam((*it).m_teamID));
	}
}

ZStream& Classification::Serialize(ZStream& o_stream) const
{
	o_stream.WriteOpenTag("Classification");

	// teamscores
	o_stream.WriteOpenTag("TeamScores");
	o_stream.WriteAttribute("NumTeams", m_teamScores.size());
	for(TeamScoreCollection::const_iterator it = m_teamScores.begin(); it != m_teamScores.end(); ++it)
		(*it).Serialize(o_stream);
	o_stream.WriteCloseTag("TeamScores");

	// matchdata
	o_stream.WriteOpenTag("PlayedMatches");
	o_stream.WriteAttribute("NumMatches", m_matchHistory.size());
	for(MatchDataVecCIt it = m_matchHistory.begin(); it != m_matchHistory.end(); ++it)
		(*it)->Serialize(o_stream);
	o_stream.WriteCloseTag("PlayedMatches");

	o_stream.WriteCloseTag("Classification");
	return o_stream;
}

ZStream& Classification::Deserialize(ZStream& i_stream)
{
	i_stream.SkipLine();
	size_t size;

	// teamscores
	i_stream.SkipLine();
	i_stream.ReadAttribute(size);
	m_teamScores.clear();
	m_teamScores.resize(size);
	for(TeamScoreCollection::iterator it= m_teamScores.begin(); it != m_teamScores.end(); ++it)
		it->Deserialize(i_stream);
	i_stream.SkipLine();
	
	// matchdata
	i_stream.SkipLine();
	i_stream.ReadAttribute(size);
	Utilities::DeepClear(m_matchHistory);
	m_matchHistory.resize(size);
	for(MatchDataVecIt it = m_matchHistory.begin(); it != m_matchHistory.end(); ++it)
	{
		*it = new MatchData;
		(*it)->Deserialize(i_stream);
	}
	i_stream.SkipLine();
	
	i_stream.SkipLine();
	return i_stream;
}

void TeamScore::Serialize(ZStream& o_stream) const
{
	o_stream.WriteOpenTag("TeamScore");
	o_stream.WriteAttribute("TeamID", m_teamID);
	o_stream.WriteAttribute("Score", m_score);
	o_stream.WriteAttribute("Win", m_winMatches);
	o_stream.WriteAttribute("Lost", m_lostMatches);
	o_stream.WriteAttribute("Draw", m_drawMathes);
	o_stream.WriteAttribute("InGoal", m_inGoals);
	o_stream.WriteAttribute("OutGoal", m_outGoals);
	o_stream.WriteCloseTag("TeamScore");
}

void TeamScore::Deserialize(ZStream& i_stream)
{
	i_stream.SkipLine();
	i_stream.ReadAttribute(m_teamID);
	i_stream.ReadAttribute(m_score);
	i_stream.ReadAttribute(m_winMatches);
	i_stream.ReadAttribute(m_lostMatches);
	i_stream.ReadAttribute(m_drawMathes);
	i_stream.ReadAttribute(m_inGoals);
	i_stream.ReadAttribute(m_outGoals);
	i_stream.SkipLine();
}
